﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Web;
using System.IO;

namespace KanbanizeAPI
{
    /// <summary>
    /// Available return formats for requests.
    /// </summary>
    public enum ResultFormat
    {
        XML,
        JSON,
        CSV
    }

    /// <summary>
    /// Task priorities when working specifically with tasks.
    /// </summary>
    public enum TaskPriority
    {
        Low,
        Average,
        High
    }

    /// <summary>
    /// History choices when querying for items that allow history results.
    /// </summary>
    public enum HistoryChoice
    {
        Yes,
        No
    }

    /// <summary>
    /// Block event choices when working with request that allow block status.
    /// </summary>
    public enum BlockEvent
    {
        Block,
        Unblock,
        EditBlock
    }
    /// <summary>
    /// Connector for pulling data from the Kanbanize! API. See http://kanbanize.com/ctrl_integration for more detail.
    /// </summary>
    public class KanbanizeConnector
    {
        /// <summary>
        /// Available Kanbanize API address and operations.
        /// </summary>
        private const string ApiAddress = "http://kanbanize.com/index.php/api/kanbanize";
        private const string LOGIN = "/login/email/{0}/pass/{1}";
        private const string GET_PROJECTS_AND_BOARDS = "/get_projects_and_boards";
        private const string GET_BOARD_STRUCTURE = "/get_board_structure/boardid/{0}";
        private const string GET_BOARD_SETTINGS = "/get_board_settings/boardid/{0}";
        private const string CREATE_NEW_TASK = "/create_new_task/boardid/{0}/title/{1}/description/{2}/priority/{3}/assignee/{4}/color/{5}/size/{6}/tags/{7}/deadline/{8}";
        private const string DELETE_TASK = "/delete_task/boardid/{0}/taskid/{1}";
        private const string GET_TASK_DETAILS = "/get_task_details/boardid/{0}/taskid/{1}/history/{2}";
        private const string GET_ALL_TASKS = "/get_all_tasks/boardid/{0}";
        private const string ADD_COMMENT = "/add_comment/taskid/{0}/comment/{1}";
        private const string MOVE_TASK = "/move_task/boardid/{0}/taskid/{1}/column/{2}";
        private const string EDIT_TASK = "/edit_Task/boardid/{0}/taskid/{1}/title/{2}/description/{3}/priority/{4}/assignee/{5}/color/{6}/size/{7}/tags/{8}/deadline/{9}";
        private const string BLOCKEDIT_TASK = "/block_task/boardid/{0}/taskid/{1}/event/{2}/blockreason/{3}";
        private const string UNBLOCK_TASK = "/block_task/boardid/{0}/taskid/{1}/event/{2}";
        
        private string ApiKey;
        private string ApiEmail;
        private string ApiPass;

        private ResultFormat Format = ResultFormat.XML;


        /// <summary>
        /// Constructor for the Kanbanize API access object.
        /// </summary>
        /// <param name="email">The email username to use.</param>
        /// <param name="pass">The password for the email username.</param>
        /// <param name="key">The unique API key required for accessing the API.</param>
        /// <param name="resultFormat">The format in which to return data requested of the API, default is ResultFormat.XML</param>
        public KanbanizeConnector(string email, string pass, string key, ResultFormat resultFormat = ResultFormat.XML)
        {
            ApiEmail = email;
            ApiPass = pass;
            ApiKey = key;

            Format = resultFormat;
        }

        /// <summary>
        /// Authenticates with the Kanbanize API.
        /// </summary>
        /// <returns></returns>
        public string Login()
        {
            return Post(string.Format(LOGIN, HttpUtility.UrlEncode(ApiEmail), ApiPass));
        }

        /// <summary>
        /// Requests Projects and Boards.
        /// </summary>
        /// <returns>Arrays of projects and boards.</returns>
        public string GetProjectsAndBoards()
        {
            return Post(GET_PROJECTS_AND_BOARDS);
        }

        /// <summary>
        /// Requests board structure.
        /// </summary>
        /// <param name="boardId">The Id of the board to operate on.</param>
        /// <returns>Arrays of columns and lanes.</returns>
        public string GetBoardStructure(int boardId)
        {
            return Post(string.Format(GET_BOARD_STRUCTURE, boardId));
        }

        /// <summary>
        /// Requests board settings.
        /// </summary>
        /// <param name="boardId">The Id of the board to operate on.</param>
        /// <returns>Usernames, templates, and types associated with the boardId requsted.</returns>
        public string GetBoardSettings(int boardId)
        {
            return Post(string.Format(GET_BOARD_SETTINGS, boardId));
        }

        /// <summary>
        /// Creates a new task card.
        /// </summary>
        /// <param name="boardId">The Id of the board to operate on.</param>
        /// <param name="title">The Title of the task to be created.</param>
        /// <param name="description">The Description of the task being created.</param>
        /// <param name="priority">The TaskPriority (low, average, high) of the task being created.</param>
        /// <param name="assignee">The Assignee of the task being created.</param>
        /// <param name="colorCode">The hexadecimal color code of the card being created, without leading #.</param>
        /// <param name="size">The (work effort) Size of the card being created.</param>
        /// <param name="tags">Tags associated with the card being created</param>
        /// <param name="deadline">The Deadline date of the card being created.</param>
        /// <returns>The Id of the new task card being created.</returns>
        public string CreateNewTask(int boardId, string title, string description, TaskPriority priority, KanbanizeUser assignee, string colorCode, int size, string tags, DateTime deadline)
        {
            return Post(string.Format(CREATE_NEW_TASK, boardId, title, description, priority.ToString(), assignee.Username, colorCode, size, tags, deadline.ToString("yyyy-MM-dd")));
        }

        /// <summary>
        /// Deletes a card from a board.
        /// </summary>
        /// <param name="boardId">The Id of the board to operate on.</param>
        /// <param name="taskId">The Id of the task card to operate on.</param>
        /// <returns>The status of the operation; 1 or error.</returns>
        public string DeleteTask(int boardId, int taskId)
        {
            return Post(string.Format(DELETE_TASK, boardId, taskId));
        }

        /// <summary>
        /// Retrieves details for a task.
        /// </summary>
        /// <param name="boardId">The Id of the board to operate on.</param>
        /// <param name="taskId">The Id of the task to operate on.</param>
        /// <param name="historyChoice">A Yes or No choice for requesting card history data. Default is HistoryChoice.Yes.</param>
        /// <returns>The details for the queried task.</returns>
        public string GetTaskDetails(int boardId, int taskId, HistoryChoice historyChoice = HistoryChoice.Yes)
        {
            return Post(string.Format(GET_TASK_DETAILS, boardId, taskId, historyChoice.ToString()));
        }

        /// <summary>
        /// Retrieves all available tasks and their details from a board.
        /// </summary>
        /// <param name="boardId">The Id of the board to operate on.</param>
        /// <returns>The tasks and their details from the queried board.</returns>
        public string GetAllTasks(int boardId)
        {
            return Post(string.Format(GET_ALL_TASKS, boardId));
        }

        /// <summary>
        /// Adds a comment to a task.
        /// </summary>
        /// <param name="taskId">The Id of the task to operate on.</param>
        /// <param name="comment">The comment to add to the task.</param>
        /// <returns>The details around the addition of the comment; history event Id, author, and current date.</returns>
        public string AddComment(int taskId, string comment)
        {
            return Post(string.Format(ADD_COMMENT, taskId, comment));
        }

        /// <summary>
        /// Moves a task from its current location to the column specified.
        /// </summary>
        /// <param name="boardId">The Id of the board to be operate on.</param>
        /// <param name="taskId">The Id of the task in to be moved.</param>
        /// <param name="columnName">The name of the column to move the task into.</param>
        /// <returns>The status of the operation; 1 or error.</returns>
        public string MoveTask(int boardId, int taskId, string columnName)
        {
            return Post(string.Format(MOVE_TASK, boardId, taskId, columnName));
        }

        /// <summary>
        /// Creates a new task card.
        /// </summary>
        /// <param name="boardId">The Id of the board to operate on.</param>
        /// <param name="taskId">The Id of the task to operate on.</param>
        /// <param name="title">The Title of the task to be edited.</param>
        /// <param name="description">The Description of the task being edited.</param>
        /// <param name="priority">The TaskPriority (low, average, high) of the task being edited.</param>
        /// <param name="assignee">The Assignee of the task being edited.</param>
        /// <param name="colorCode">The hexadecimal color code of the card being edited, without leading #.</param>
        /// <param name="size">The (work effort) Size of the card being edited.</param>
        /// <param name="tags">Tags associated with the card being edited</param>
        /// <param name="deadline">The Deadline date of the card being edited.</param>
        /// <returns>The status of the operation; 1 or error.</returns>
        public string EditTask(int boardId, int taskId, string title, string description, TaskPriority priority, KanbanizeUser assignee, string colorCode, int size, string tags, DateTime deadline)
        {
            return Post(string.Format(EDIT_TASK, boardId, taskId, title, description, priority.ToString(), assignee.Username, colorCode, size, tags, deadline.ToString("yyyy-MM-dd")));
        }

        /// <summary>
        /// Blocks a card.
        /// </summary>
        /// <param name="boardId">The Id of the board to be operated on.</param>
        /// <param name="taskId">The Id of the task to block.</param>
        /// <param name="blockEvent">The type of block event to perform.</param>
        /// <param name="blockReason">The reason for blocking a card, required if blockEvent is BlockEvent.Block or BlockEvent.EditBlock</param>
        /// <returns>The status of the operation; 1 or error.</returns>
        public string BlockTask(int boardId, int taskId, BlockEvent blockEvent, string blockReason)
        {
            switch (blockEvent)
            {
                case BlockEvent.Block:
                case BlockEvent.EditBlock:
                    return Post(string.Format(BLOCKEDIT_TASK, boardId, taskId, blockEvent.ToString().ToLower(), blockReason));
                default:
                    return Post(string.Format(UNBLOCK_TASK, boardId, taskId, blockEvent.ToString().ToLower()));
            }            
        }

        /// <summary>
        /// Post function for uploading API requests to the Kanbanize! API.
        /// </summary>
        /// <param name="operation">The operation to be performed.</param>
        /// <returns>The API response to the requested operation in the ResultFormat as set by the Format class member.</returns>
        public string Post(string operation)
        {
            using (WebClient serviceRequest = new WebClient())
            {
                try
                {
                    serviceRequest.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
                    serviceRequest.Headers.Add("apikey", ApiKey);

                    string requestString = string.Format("{0}{1}/format/{2}", ApiAddress, operation, Format.ToString().ToLower());

                    Logger.Log("API Request: " + requestString);

                    return serviceRequest.UploadString(new Uri(requestString), string.Empty);

                }
                catch (Exception e)
                {
                    Logger.Log("Post error: " + e.Message);
                    return string.Format("<xml><error>Post error in KanbanizeConnector.</error><errorMessage>{0}</errorMessage></xml>", e.Message);
                }
            }
        }
    }
}
